package com.zx.cloud.util;

import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;

public final class DateUtilities {

	private static final String days = "(monday|mon|tuesday|tues|tue|wednesday|wed|thursday|thur|thu|friday|fri|saturday|sat|sunday|sun)"; // longer
	private static final String mos = "(Jan|January|Feb|February|Mar|March|Apr|April|May|Jun|June|Jul|July|Aug|August|Sep|Sept|September|Oct|October|Nov|November|Dec|December)";
	private static final Pattern datePattern1 = Pattern.compile("(\\d{4})[./-](\\d{1,2})[./-](\\d{1,2})");
	private static final Pattern datePattern2 = Pattern.compile("(\\d{1,2})[./-](\\d{1,2})[./-](\\d{4})");
	private static final Pattern datePattern3 = Pattern.compile(mos + "[ ]*[,]?[ ]*(\\d{1,2})(st|nd|rd|th|)[ ]*[,]?[ ]*(\\d{4})", Pattern.CASE_INSENSITIVE);
	private static final Pattern datePattern4 = Pattern
			.compile("(\\d{1,2})(st|nd|rd|th|)[ ]*[,]?[ ]*" + mos + "[ ]*[,]?[ ]*(\\d{4})", Pattern.CASE_INSENSITIVE);
	private static final Pattern datePattern5 = Pattern
			.compile("(\\d{4})[ ]*[,]?[ ]*" + mos + "[ ]*[,]?[ ]*(\\d{1,2})(st|nd|rd|th|)", Pattern.CASE_INSENSITIVE);
	private static final Pattern datePattern6 = Pattern.compile(days + "[ ]+" + mos + "[ ]+(\\d{1,2})[ ]+(\\d{2}:\\d{2}:\\d{2})[ ]+[A-Z]{1,3}\\s+(\\d{4})",
			Pattern.CASE_INSENSITIVE);
	private static final Pattern timePattern1 = Pattern.compile("(\\d{2})[:.](\\d{2})[:.](\\d{2})[.](\\d{1,10})([+-]\\d{2}[:]?\\d{2}|Z)?");
	private static final Pattern timePattern2 = Pattern.compile("(\\d{2})[:.](\\d{2})[:.](\\d{2})([+-]\\d{2}[:]?\\d{2}|Z)?");
	private static final Pattern timePattern3 = Pattern.compile("(\\d{2})[:.](\\d{2})([+-]\\d{2}[:]?\\d{2}|Z)?");
	private static final Pattern dayPattern = Pattern.compile(days, Pattern.CASE_INSENSITIVE);
	private static final Map<String, String> months = new LinkedHashMap<String, String>();

	static {
		months.put("jan", "1");
		months.put("january", "1");
		months.put("feb", "2");
		months.put("february", "2");
		months.put("mar", "3");
		months.put("march", "3");
		months.put("apr", "4");
		months.put("april", "4");
		months.put("may", "5");
		months.put("jun", "6");
		months.put("june", "6");
		months.put("jul", "7");
		months.put("july", "7");
		months.put("aug", "8");
		months.put("august", "8");
		months.put("sep", "9");
		months.put("sept", "9");
		months.put("september", "9");
		months.put("oct", "10");
		months.put("october", "10");
		months.put("nov", "11");
		months.put("november", "11");
		months.put("dec", "12");
		months.put("december", "12");
	}

	public static Date parseDate(String dateStr) {
		if (dateStr == null) {
			return null;
		}
		dateStr = dateStr.trim();
		if ("".equals(dateStr)) {
			return null;
		}
		Matcher matcher = datePattern1.matcher(dateStr);
		String year, month = null, day, mon = null, remains;
		if (matcher.find()) {
			year = matcher.group(1);
			month = matcher.group(2);
			day = matcher.group(3);
			remains = matcher.replaceFirst("");
		} else {
			matcher = datePattern2.matcher(dateStr);
			if (matcher.find()) {
				month = matcher.group(1);
				day = matcher.group(2);
				year = matcher.group(3);
				remains = matcher.replaceFirst("");
			} else {
				matcher = datePattern3.matcher(dateStr);
				if (matcher.find()) {
					mon = matcher.group(1);
					day = matcher.group(2);
					year = matcher.group(4);
					remains = matcher.replaceFirst("");
				} else {
					matcher = datePattern4.matcher(dateStr);
					if (matcher.find()) {
						day = matcher.group(1);
						mon = matcher.group(3);
						year = matcher.group(4);
						remains = matcher.replaceFirst("");
					} else {
						matcher = datePattern5.matcher(dateStr);
						if (matcher.find()) {
							year = matcher.group(1);
							mon = matcher.group(2);
							day = matcher.group(3);
							remains = matcher.replaceFirst("");
						} else {
							matcher = datePattern6.matcher(dateStr);
							if (!matcher.find()) {
								error("Unable to parse: " + dateStr);
							}
							year = matcher.group(5);
							mon = matcher.group(2);
							day = matcher.group(3);
							remains = matcher.group(4);
						}
					}
				}
			}
		}
		if (mon != null) {
			month = months.get(mon.trim().toLowerCase());
		}
		String hour = null, min = null, sec = "00", milli = "0", tz = null;
		remains = remains.trim();
		matcher = timePattern1.matcher(remains);
		if (matcher.find()) {
			hour = matcher.group(1);
			min = matcher.group(2);
			sec = matcher.group(3);
			milli = matcher.group(4);
			if (matcher.groupCount() > 4) {
				tz = matcher.group(5);
			}
		} else {
			matcher = timePattern2.matcher(remains);
			if (matcher.find()) {
				hour = matcher.group(1);
				min = matcher.group(2);
				sec = matcher.group(3);
				if (matcher.groupCount() > 3) {
					tz = matcher.group(4);
				}
			} else {
				matcher = timePattern3.matcher(remains);
				if (matcher.find()) {
					hour = matcher.group(1);
					min = matcher.group(2);
					if (matcher.groupCount() > 2) {
						tz = matcher.group(3);
					}
				} else {
					matcher = null;
				}
			}
		}
		if (matcher != null) {
			remains = matcher.replaceFirst("");
		}
		if (StringUtils.length(remains) > 0) {
			Matcher dayMatcher = dayPattern.matcher(remains);
			if (dayMatcher.find()) {
				remains = dayMatcher.replaceFirst("").trim();
			}
		}
		if (StringUtils.length(remains) > 0) {
			remains = remains.trim();
			if (!remains.equals(",") && (!remains.equals("T"))) {
				error("Issue parsing data/time, other characters present: " + remains);
			}
		}
		Calendar c = Calendar.getInstance();
		c.clear();
		if (tz != null) {
			if ("z".equalsIgnoreCase(tz)) {
				c.setTimeZone(TimeZone.getTimeZone("GMT"));
			} else {
				c.setTimeZone(TimeZone.getTimeZone("GMT" + tz));
			}
		}
		int y = Integer.parseInt(year);
		int m = Integer.parseInt(month) - 1;
		int d = Integer.parseInt(day);
		if (m < 0 || m > 11) {
			error("Month must be between 1 and 12 inclusive, date: " + dateStr);
		}
		if (d < 1 || d > 31) {
			error("Day must be between 1 and 31 inclusive, date: " + dateStr);
		}
		if (matcher == null) {
			c.set(y, m, d);
		} else {
			int h = Integer.parseInt(hour);
			int mn = Integer.parseInt(min);
			int s = Integer.parseInt(sec);
			int ms = Integer.parseInt(milli);
			if (h > 23) {
				error("Hour must be between 0 and 23 inclusive, time: " + dateStr);
			}
			if (mn > 59) {
				error("Minute must be between 0 and 59 inclusive, time: " + dateStr);
			}
			if (s > 59) {
				error("Second must be between 0 and 59 inclusive, time: " + dateStr);
			}
			c.set(y, m, d, h, mn, s);
			c.set(Calendar.MILLISECOND, ms);
		}
		return c.getTime();
	}

	private static void error(String msg) {
		throw new IllegalArgumentException(msg);
	}
}
